#pragma once
#include "count.h"
#include "gc.h"
#include <atomic>
#include <cstring>
#include <iostream>
#include <memory.h>
#include <mutex>
#include <stdlib.h>
#include <unistd.h>
#include <unordered_map>
#include <unordered_set>

class GC {
public:
  enum GCMode {
    Insert,
    Delete,
    Mixed,
  } const mode_ = Delete;

private:
  std::atomic<bool> is_run_;
  std::unordered_set<void *> root_, all_memory_, self_delete_;
  std::unordered_set<void *> white_, grey_, black_;
  std::mutex mut, muta;
  GC() : is_run_(false) {}

public:
  GC(const GC &) = delete;
  GC &operator=(const GC &) = delete;
  static GC &GetInstance() {
    static GC gc;
    return gc;
  }
  void InsertBarrier(void *self, void *iptr) {
    if (!is_run_) {
      return;
    }
    if (black_.find(self) != black_.end()) {
      grey_.insert(iptr);
    }
  }
  void DeleteBarrier(void *self, void *dptr) {
    if (!is_run_) {
      return;
    }
    if (black_.find(self) != black_.end()) {
      grey_.insert(dptr);
    }
  }
  void Run() {
    if (is_run_) {
      return;
    }
    is_run_ = true;
    grey_.clear();
    white_.clear();
    black_.clear();

    mut.lock();
    auto iter = root_.begin();
    while (iter != root_.end()) {
      grey_.insert(*iter);
      iter++;
    }
    mut.unlock();

    muta.lock();
    auto iter1 = all_memory_.begin();
    while (iter1 != all_memory_.end()) {
      if (grey_.find(*iter1) == grey_.end()) {
        white_.insert(*iter1);
      }
      iter1++;
    }
    muta.unlock();

    while (grey_.size() != 0) {
      auto copy = grey_;
      grey_.clear();
      auto iter = copy.begin();
      while (iter != copy.end()) {
        black_.insert(*iter);
        if (*(void **)(*iter) != NULL &&
            black_.find(*(void **)(*iter)) == black_.end()) {
          grey_.insert(*(void **)(*iter));
          white_.erase(*(void **)(*iter));
        }
        iter++;
      }
    }
    Clean();
    is_run_ = false;
  }
  void InsertRoot(void *ptr) {
    mut.lock();
    root_.insert(ptr);
    mut.unlock();
  }
  void InsertAll(void *ptr) {
    muta.lock();
    all_memory_.insert(ptr);
    muta.unlock();
  }
  void EraseRoot(void *ptr) {
    mut.lock();
    root_.erase(ptr);
    mut.unlock();
  }
  void EraseAll(void *ptr) {
    muta.lock();
    all_memory_.erase(ptr);
    muta.unlock();
  }

private:
  void Clean() {
    auto iter = white_.begin();
    while (iter != white_.end()) {
      count.delDict(*iter, Count::MemStruct::GC);
      EraseAll(*iter);
      free(*iter);
      iter++;
    }
  }
};

class Ptr {
private:
  void **ptr_;// 每一个ptr对象都会分配一个堆的内存,这个是指向堆内存的指针
  bool is_auto_;

public:
  Ptr(bool is_stack = false, bool is_gloab = false, int id = 0)
      : ptr_(NULL), is_auto_(false) {
    ptr_ = (void **)malloc(sizeof(void *));
    memset(ptr_, 0, sizeof(void *));
    GC::GetInstance().InsertAll(ptr_);
    if (is_stack || is_gloab) {
      GC::GetInstance().InsertRoot(ptr_);
      is_auto_ = is_stack;
    }

    count.insertDict(ptr_, is_stack, is_gloab, id);
  }
  ~Ptr() {
    if (is_auto_) {
      count.delDict(ptr_, Count::MemStruct::Auto);

      GC::GetInstance().EraseRoot(ptr_);
      GC::GetInstance().EraseAll(ptr_);
      free(ptr_);
    }
  }
  void PointTo(Ptr &new_ptr) {
    if (GC::GetInstance().mode_ == GC::Insert) {
      GC::GetInstance().InsertBarrier(this->ptr_, new_ptr.ptr_);
    } else if (GC::GetInstance().mode_ == GC::Delete) {
      GC::GetInstance().DeleteBarrier(this->ptr_, *this->ptr_);
    }
    *ptr_ = (int *)new_ptr.ptr_;
  }
};
